package com.em.tuppence.backend.services;

import java.util.Date;

import org.infinispan.Cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.em.tuppence.backend.infinispan.InfinispanManager;
import com.em.tuppence.core.model.application.User;
import com.em.tuppence.core.model.exceptions.TuppenceUserAlreadyExistsException;
import com.em.tuppence.crypto.CryptoProvider;
import com.em.tuppence.services.IUserService;

public class UserService implements IUserService {

	private static Logger logger = LoggerFactory.getLogger(UserService.class);
	
	private static Cache<String, User> userCache = InfinispanManager.INSTANCE.getCache("tuppence-user-cache", String.class, User.class);
	
	public UserService() {
		
	}
	
	public User getUser(String userOpenIdUrl) {
		
		//get id
		String id = CryptoProvider.generateId(userOpenIdUrl);
		
		//log
		UserService.logger.info("Queried for user: {} with id {}", userOpenIdUrl, id);
			
		//ask cache for user
		User foundUser = UserService.userCache.get(id);
		if(foundUser != null) {
			UserService.logger.info("Found user: {} / {}", userOpenIdUrl, id);
		} else {
			UserService.logger.info("No user found.");
		}
		
		return foundUser;
	}
	
	public void addUser(User user) throws TuppenceUserAlreadyExistsException {		
		//get id for potential existing user (collision) and then look up that user
		String existingId = CryptoProvider.generateId(user.getOpenIdUrl());
				
		//if the user exists or appears to exist then investigate
		if(UserService.userCache.containsKey(existingId)) {
			User existingUser = this.getUser(existingId);	
			
			UserService.logger.info("User already exists with id: " + user.getId());
			
			//check and see if the users are actually the same or if we've hit the vanishingly small possibility of an SHA512/digest colision
			if(!existingUser.getOpenIdUrl().equalsIgnoreCase(user.getOpenIdUrl())) {
				//use the url as salt for the digest... not the greatest idea but shouldn't collide again
				user.setId(CryptoProvider.generateId(user.getOpenIdUrl(),user.getOpenIdUrl()));
			} else {
				//we haven't and this user is trying to register twice (how did we get here... it should have just let them log in?)
				throw new TuppenceUserAlreadyExistsException();
			}
		} else {
			//set the public id
			user.setId(CryptoProvider.generateId(user.getOpenIdUrl()));
		}
		
		//set the private/secret password/key for this user	
		user.setSecret(CryptoProvider.generateSecret());		
		
		//set date and time of user registration (server time now)
		user.setRegisteredTimestamp(new Date());
		
		//add user, but do not (ever) overwrite
		UserService.userCache.putIfAbsent(user.getId(), user);
		
		UserService.logger.info("Added user with id: " + user.getId());
	}
	
	public void updateUser(User user) {
		UserService.userCache.put(user.getId(), user);
		
		UserService.logger.info("Updated user with id: " + user.getId());
	}
	
	public void removeUser(User user) {
		UserService.userCache.remove(user.getId());
		
		UserService.logger.info("Removed user with id: " + user.getId());
	}
	
}
